/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;

/**
 *
 * @author Main
 */
public class Bar extends JComponent implements MouseListener, MouseMotionListener {
    public final static int STATE_RELEASED = 0;
    public final static int STATE_HOVER = 1;
    public final static int STATE_PRESSED = 2;
    private int curState = STATE_RELEASED;
    private boolean disabled = false;
    private boolean editable;
    private double curValue;
    private double[] range;
    private String label;
    private Color color;
    private Color darker;
    private boolean vertical;
    private int mouseX = -1;
    private int mouseY = -1;
    private boolean negative = false;
    private Color xor;
    private List<ActionListener> listeners;
    private ActionEvent action;
    private boolean dragging = false;

    public Bar() {
        this(20, 100, new double[] { 0.0, 100.0 }, "bar", true, true);
        listeners = new ArrayList<ActionListener>();
        action = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, label);
    }

    public void addActionListener(ActionListener l) { listeners.add(l); }
    public void removeActionListener(ActionListener l) { listeners.remove(l); }

    public Bar(int width, int height, double[] range, String label, boolean vertical, boolean editable) {
        this.setPreferredSize(new Dimension(width, height));
        this.vertical = vertical;
        this.range = range;
        this.label = label;
        this.editable = editable;
        curValue = range[0];
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
    }
    
    public void setColor(Color color) {
        this.color = color;
        this.xor = new Color(color.getRed()^255, color.getGreen()^255, color.getBlue()^255);
        this.darker = color.darker();
        repaint();
    }
    
    public void setValue(double value) {
        curValue = value;
        repaint();
    }

    public double getValue() { return curValue; }

    @Override
    public void paintComponent(Graphics g) {
        int bar = calcBarHeight();

        //background
        if (negative) g.setColor(color);
        else g.setColor(Color.BLACK);
        if (vertical)
            g.fillRect(0, 0, this.getWidth(), this.getHeight()-bar);
        else g.fillRect(bar, 0, this.getWidth(), this.getHeight());

        //bar
        if (editable) {
            if (curState==STATE_PRESSED)
                g.setColor(color);
            if (curState==STATE_HOVER);
                g.setColor(darker);
        } else {
            if (negative) g.setColor(Color.BLACK);
            else g.setColor(color);
        }
        if (vertical) {
            g.fillRect(0, this.getHeight()-bar, this.getWidth(), bar);
        } else g.fillRect(0, 0, bar, this.getHeight());

        //draw position
        if (editable) {
            if (curState==STATE_HOVER) {
                if (vertical) {
                    if (mouseY<getHeight()-bar) // position is on the bright area
                        g.setColor(darker);
                    else g.setColor(color);
                } else {
                    if (mouseX>bar)
                        g.setColor(darker);
                    else g.setColor(color);
                }
            } else if (curState==STATE_PRESSED) {
                g.setColor(xor);
            }
            if (curState==STATE_HOVER || curState==STATE_PRESSED ) { // draw position 
                if (vertical)
                    g.drawLine(1, mouseY, this.getWidth()-2, mouseY);
                else g.drawLine(mouseX, 1, mouseX, this.getHeight()-2);
            }
        }

        //border
        if (curState==STATE_HOVER && editable)
            g.setColor(color);
        else if (curState==STATE_PRESSED && editable)
            g.setColor(xor);
        else
            g.setColor(darker);
        g.drawRect(0, 0, getWidth()-1, getHeight()-1);

    }
       
    public void setEditable(boolean editable) {
        this.editable = editable;
        repaint();
    }

    public boolean isEditable() { return editable; }

    public void setLabel(String label) {
        this.label = label;
    } public String getLabel() { return label; }

    public void setNegative(boolean negative) {
        this.negative = negative;
        repaint();
    }

    public void setRange(double[] range) {
        this.range = range;
        repaint();
    } public double[] getRange() { return range; }

    public void setVertical(boolean vertical) {
        this.vertical = vertical;
        repaint();
    } public boolean isVertical() { return vertical; }

    private int calcBarHeight() {
        int height;
        if (vertical)
            height = (int)Math.round((this.getHeight()/range[1]-range[0])*curValue);
        else height = (int)Math.round((this.getWidth()/range[1]-range[0])*curValue);
        return height;
    }
    
    private double xToValue(int x, int max) {
        int pixCount = Math.abs(x-max);
        double rangeSize = Math.abs(range[0]-range[1]);
        return (pixCount*rangeSize) / max + range[0];
    }

    public void mouseExited(MouseEvent e) {
        curState = STATE_RELEASED;
        repaint();

        dragging = false;
    }

    public void mousePressed(MouseEvent e) {
        if (!disabled) {
            curState = STATE_PRESSED;
            if (editable) {
                int dist, max;
                if (vertical) {
                    dist = e.getY(); max = getHeight();
                    if (dist>max) dist = max;
                    if (dist<0) dist = 0;
                }
                else {
                    dist = getWidth()-e.getX(); max = getWidth();
                    if (dist>max) dist = max;
                    if (dist<0) dist = 0;
                }

                this.setValue(xToValue(dist, max));
                for (ActionListener listener : listeners)
                    listener.actionPerformed(action);
            }
        }
    }

    public void mouseReleased(MouseEvent e) {
        if (!disabled) {
            curState = STATE_RELEASED;
            repaint();
        }
    }

    public void mouseEntered(MouseEvent e) {
        if (!disabled) {
            curState = STATE_HOVER;
            repaint();
        }
    }

    public void mouseDragged(MouseEvent e) {
        dragging = true;
        if (e.getX()>getWidth())
            mouseX = getWidth();
        else if (e.getX()<0)
            mouseX = 0;
        else
            mouseX = e.getX();

        if (e.getY()>getHeight())
            mouseY = getHeight();
        else if (e.getY()<0)
            mouseY = 0;
        else
            mouseY = e.getY();
        this.mousePressed(e);
        repaint();
    }

    public void mouseMoved(MouseEvent e) {
        mouseX = e.getX();
        mouseY = e.getY();
        if (curState==STATE_RELEASED) curState=STATE_HOVER;
        repaint();
    }

    public void mouseClicked(MouseEvent e) {
    }

}